using System.Linq;
using FamilyTreeProject.Collections;
using System;

namespace FamilyTreeProject.Data
{
    /// <summary>
    /// Provides options to allow the consumer to select a page of data from a paged data store
    /// </summary>
    /// <typeparam name="T">The type of object in the data store</typeparam>
    public class PageSelector<T> where T : IIdentifiable
    {
        #region Private Members

        private IQueryable<T> source;
        private int pageSize;

        #endregion

        #region Constructors

        /// <summary>
        /// Constructs a new PageSelector for use on the specified data store
        /// </summary>
        /// <param name="source">The data store to page</param>
        /// <param name="pageSize">The size of each page</param>
        public PageSelector(IQueryable<T> source, int pageSize)
        {
            this.source = source;
            this.pageSize = pageSize;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Retrieves the specified page as a <see cref="IPagedList{T}"/>
        /// </summary>
        /// <param name="pageIndex">The index (zero-based) of the page to retrieve</param>
        /// <returns>An <see cref="IPagedList{T}"/> containing the page of data, or an empty list if the page does not exist</returns>
        public IPagedList<T> GetPage(int pageIndex)
        {
            if (pageIndex < 0)
                throw new IndexOutOfRangeException("Index cannot be negative");
            if (pageIndex * this.pageSize > (this.source.Count() - 1))
                throw new IndexOutOfRangeException("Index * PageSize cannot be larger than the number of records.");

            // Need to enforce a determinate ordering to the entities for paging, so we order by ID
            // Since this is the last query operation (before Take and Skip), we won't conflict
            // with other OrderBy calls.
            return new PagedList<T>(source.OrderBy(entity => entity.Id), pageIndex, pageSize);
        }

        #endregion
    }
}
